home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / lib / tk2.3 / dist / tkTextDisp.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-08-24  |  60.5 KB  |  2,036 lines

  1. /* 
  2.  * tkTextDisp.c --
  3.  *
  4.  *    This module provides facilities to display text widgets.  It is
  5.  *    the only place where information is kept about the screen layout
  6.  *    of text widgets.
  7.  *
  8.  * Copyright 1992 Regents of the University of California.
  9.  * Permission to use, copy, modify, and distribute this
  10.  * software and its documentation for any purpose and without
  11.  * fee is hereby granted, provided that the above copyright
  12.  * notice appear in all copies.  The University of California
  13.  * makes no representations about the suitability of this
  14.  * software for any purpose.  It is provided "as is" without
  15.  * express or implied warranty.
  16.  */
  17.  
  18. #ifndef lint
  19. static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkTextDisp.c,v 1.20 92/08/24 09:24:18 ouster Exp $ SPRITE (Berkeley)";
  20. #endif
  21.  
  22. #include "tkConfig.h"
  23. #include "tkInt.h"
  24. #include "tkText.h"
  25.  
  26. /*
  27.  * The following structure describes how to display a range of characters.
  28.  * The information is generated by scanning all of the tags associated
  29.  * with the characters and combining that with default information for
  30.  * the overall widget.  These structures form the hash keys for
  31.  * dInfoPtr->styleTable.
  32.  */
  33.  
  34. typedef struct StyleValues {
  35.     Tk_3DBorder border;        /* Used for drawing background under text.
  36.                  * NULL means use widget background. */
  37.     int borderWidth;        /* Width of 3-D border for background. */
  38.     int relief;            /* 3-D relief for background. */
  39.     Pixmap bgStipple;        /* Stipple bitmap for background.  None
  40.                  * means draw solid. */
  41.     XColor *fgColor;        /* Foreground color for text. */
  42.     XFontStruct *fontPtr;    /* Font for displaying text. */
  43.     Pixmap fgStipple;        /* Stipple bitmap for text and other
  44.                  * foreground stuff.   None means draw
  45.                  * solid.*/
  46.     int underline;        /* Non-zero means draw underline underneath
  47.                  * text. */
  48. } StyleValues;
  49.  
  50. /*
  51.  * The following structure extends the StyleValues structure above with
  52.  * graphics contexts used to actually draw the characters.  The entries
  53.  * in dInfoPtr->styleTable point to structures of this type.
  54.  */
  55.  
  56. typedef struct Style {
  57.     int refCount;        /* Number of times this structure is
  58.                  * referenced in Chunks. */
  59.     GC bgGC;            /* Graphics context for background.  None
  60.                  * unless background is stippled. */
  61.     GC fgGC;            /* Graphics context for foreground. */
  62.     StyleValues *sValuePtr;    /* Raw information from which GCs were
  63.                  * derived. */
  64.     Tcl_HashEntry *hPtr;    /* Pointer to entry in styleTable.  Used
  65.                  * to delete entry. */
  66. } Style;
  67.  
  68. /*
  69.  * The following structure describes a range of characters, all on the
  70.  * same line of the display (which also means the same line of the text
  71.  * widget) and all having the same display attributes.
  72.  */
  73.  
  74. typedef struct Chunk {
  75.     char *text;            /* Characters to display. */
  76.     int numChars;        /* Number of characters to display. */
  77.     Style *stylePtr;        /* Style information used to display
  78.                  * characters. */
  79.     int x;            /* X-coordinate of pixel at which to display
  80.                  * the characters. */
  81.     struct Chunk *nextPtr;    /* Next in list of all chunks displayed on the
  82.                  * same display line. */
  83. } Chunk;
  84.  
  85. /*
  86.  * The following structure describes one line of the display, which may
  87.  * be either part or all of one line of the text.
  88.  */
  89.  
  90. typedef struct DLine {
  91.     TkTextLine *linePtr;    /* Pointer to structure in B-tree that
  92.                  * contains characters displayed in this
  93.                  * line. */
  94.     int y;            /* Y-position at which line is supposed to
  95.                  * be drawn (topmost pixel of rectangular
  96.                  * area occupied by line). */
  97.     int oldY;            /* Y-position at which line currently
  98.                  * appears on display.  -1 means line isn't
  99.                  * currently visible on display.  This is
  100.                  * used to move lines by scrolling rather
  101.                  * than re-drawing. */
  102.     int height;            /* Height of line, in pixels. */
  103.     int baseline;        /* Offset of text baseline from y. */
  104.     Chunk *chunkPtr;        /* Pointer to first chunk in list of all
  105.                  * of those that are displayed on this
  106.                  * line of the screen. */
  107.     struct DLine *nextPtr;    /* Next in list of all display lines for
  108.                  * this window.   The list is sorted in
  109.                  * order from top to bottom.  Note:  the
  110.                  * next DLine doesn't always correspond
  111.                  * to the next line of text:  (a) can have
  112.                  * multiple DLines for one text line, and
  113.                  * (b) can have gaps where DLine's have been
  114.                  * deleted because they're out of date. */
  115. } DLine;
  116.  
  117. /*
  118.  * Overall display information for a text widget:
  119.  */
  120.  
  121. typedef struct DInfo {
  122.     Tcl_HashTable styleTable;    /* Hash table that maps from StyleValues to
  123.                  * Styles for this widget. */
  124.     DLine *dLinePtr;        /* First in list of all display lines for
  125.                  * this widget, in order from top to bottom. */
  126.     GC copyGC;            /* Graphics context for copying from off-
  127.                  * screen pixmaps onto screen. */
  128.     GC scrollGC;        /* Graphics context for copying from one place
  129.                  * in the window to another (scrolling):
  130.                  * differs from copyGC in that we need to get
  131.                  * GraphicsExpose events. */
  132.     int x;            /* First x-coordinate that may be used for
  133.                  * actually displaying line information.
  134.                  * Leaves space for border, etc. */
  135.     int y;            /* First y-coordinate that may be used for
  136.                  * actually displaying line information.
  137.                  * Leaves space for border, etc. */
  138.     int maxX;            /* First x-coordinate to right of available
  139.                  * space for displaying lines. */
  140.     int maxY;            /* First y-coordinate to bottom of available
  141.                  * space for displaying lines. */
  142.     int topOfEof;        /* Top-most pixel (lowest y-value) that has
  143.                  * been drawn in the appropriate fashion for
  144.                  * the portion of the window after the last
  145.                  * line of the text.  This field is used to
  146.                  * figure out when to redraw part or all of
  147.                  * the eof field. */
  148.     int flags;            /* Various flag values:  see below for
  149.                  * definitions. */
  150. } DInfo;
  151.  
  152. /*
  153.  * Flag values for DInfo structures:
  154.  *
  155.  * DINFO_OUT_OF_DATE:        Non-zero means that the DLine structures
  156.  *                for this window are partially or completely
  157.  *                out of date and need to be recomputed.
  158.  * REDRAW_PENDING:        Means that a when-idle handler has been
  159.  *                scheduled to update the display.
  160.  * REDRAW_BORDERS:        Means window border or pad area has
  161.  *                potentially been damaged and must be redrawn.
  162.  * REPICK_NEEDED:        1 means that the widget has been modified
  163.  *                in a way that could change the current
  164.  *                character (a different character might be
  165.  *                under the mouse cursor now).  Need to
  166.  *                recompute the current character before
  167.  *                the next redisplay.
  168.  */
  169.  
  170. #define DINFO_OUT_OF_DATE    1
  171. #define REDRAW_PENDING        2
  172. #define REDRAW_BORDERS        4
  173. #define REPICK_NEEDED        8
  174.  
  175. /*
  176.  * Structures of the type defined below are used to keep track of
  177.  * tags while scanning through the text to create DLine structures.
  178.  */
  179.  
  180. typedef struct TagInfo {
  181.     int numTags;        /* Number of tags currently active (the first
  182.                  * entries at *tagPtr). */
  183.     int arraySize;        /* Total number of entries at *tagPtr.  We
  184.                  * over-allocate the array to avoid continual
  185.                  * reallocations. */
  186.     TkTextTag **tagPtrs;    /* Pointer to array of pointers to active tags.
  187.                  * Array has space for arraySize tags, and
  188.                  * the first numTags are slots identify the
  189.                  * active tags. Malloc'ed (but may be NULL). */
  190.     TkTextSearch search;    /* Used to scan for tag transitions.  Current
  191.                  * state identifies next tag transition. */
  192. } TagInfo;
  193.  
  194. /*
  195.  * The following counters keep statistics about redisplay that can be
  196.  * checked to see how clever this code is at reducing redisplays.
  197.  */
  198.  
  199. static int numRedisplays;    /* Number of calls to DisplayText. */
  200. static int linesRedrawn;    /* Number of calls to DisplayDLine. */
  201. static int numCopies;        /* Number of calls to XCopyArea to copy part
  202.                  * of the screen. */
  203. static int damagedCopies;    /* Number of times that XCopyAreas didn't
  204.                  * completely work because some of the source
  205.                  * information was damaged. */
  206.  
  207. /*
  208.  * Forward declarations for procedures defined later in this file:
  209.  */
  210.  
  211. static void        ComputeStyleValues _ANSI_ARGS_((TkText *textPtr,
  212.                 int numTags, TkTextTag **tagPtr,
  213.                 StyleValues *sValuePtr));
  214. static void        DisplayDLine _ANSI_ARGS_((TkText *textPtr,
  215.                 DLine *dlPtr, Pixmap pixmap));
  216. static void        DisplayText _ANSI_ARGS_((ClientData clientData));
  217. static DLine *        FindDLine _ANSI_ARGS_((DLine *dlPtr, int line));
  218. static void        FreeDLines _ANSI_ARGS_((TkText *textPtr,
  219.                 DLine *firstPtr, DLine *lastPtr, int unlink));
  220. static void        FreeStyle _ANSI_ARGS_((Style *stylePtr));
  221. static Style *        GetStyle _ANSI_ARGS_((TkText *textPtr,
  222.                 StyleValues *sValuePtr));
  223. static DLine *        LayoutLine _ANSI_ARGS_((TkText *textPtr, int line,
  224.                 TkTextLine *linePtr, TagInfo *tInfoPtr));
  225. static void        ToggleTag _ANSI_ARGS_((TagInfo *tInfoPtr,
  226.                 TkTextTag *tagPtr));
  227. static void        UpdateDisplayInfo _ANSI_ARGS_((TkText *textPtr));
  228.  
  229. /*
  230.  *----------------------------------------------------------------------
  231.  *
  232.  * TkTextCreateDInfo --
  233.  *
  234.  *    This procedure is called when a new text widget is created.
  235.  *    Its job is to set up display-related information for the widget.
  236.  *
  237.  * Results:
  238.  *    None.
  239.  *
  240.  * Side effects:
  241.  *    A DInfo data structure is allocated and initialized and attached
  242.  *    to textPtr.
  243.  *
  244.  *----------------------------------------------------------------------
  245.  */
  246.  
  247. void
  248. TkTextCreateDInfo(textPtr)
  249.     TkText *textPtr;        /* Overall information for text widget. */
  250. {
  251.     register DInfo *dInfoPtr;
  252.     XGCValues gcValues;
  253.  
  254.     dInfoPtr = (DInfo *) ckalloc(sizeof(DInfo));
  255.     Tcl_InitHashTable(&dInfoPtr->styleTable, sizeof(StyleValues)/sizeof(int));
  256.     dInfoPtr->dLinePtr = NULL;
  257.     gcValues.graphics_exposures = False;
  258.     dInfoPtr->copyGC = Tk_GetGC(textPtr->tkwin, GCGraphicsExposures, &gcValues);
  259.     gcValues.graphics_exposures = True;
  260.     dInfoPtr->scrollGC = Tk_GetGC(textPtr->tkwin, GCGraphicsExposures,
  261.         &gcValues);
  262.     dInfoPtr->topOfEof = 0;
  263.     dInfoPtr->flags = DINFO_OUT_OF_DATE;
  264.     textPtr->dInfoPtr = dInfoPtr;
  265. }
  266.  
  267. /*
  268.  *----------------------------------------------------------------------
  269.  *
  270.  * TkTextFreeDInfo --
  271.  *
  272.  *    This procedure is called to free up all of the private display
  273.  *    information kept by this file for a text widget.
  274.  *
  275.  * Results:
  276.  *    None.
  277.  *
  278.  * Side effects:
  279.  *    Lots of resources get freed.
  280.  *
  281.  *----------------------------------------------------------------------
  282.  */
  283.  
  284. void
  285. TkTextFreeDInfo(textPtr)
  286.     TkText *textPtr;        /* Overall information for text widget. */
  287. {
  288.     register DInfo *dInfoPtr = textPtr->dInfoPtr;
  289.  
  290.     /*
  291.      * Be careful to free up styleTable *after* freeing up all the
  292.      * DLines, so that the hash table is still intact to free up the
  293.      * style-related information from the lines.  Once the lines are
  294.      * all free then styleTable will be empty.
  295.      */
  296.  
  297.     FreeDLines(textPtr, dInfoPtr->dLinePtr, (DLine *) NULL, 1);
  298.     Tcl_DeleteHashTable(&dInfoPtr->styleTable);
  299.     Tk_FreeGC(dInfoPtr->copyGC);
  300.     Tk_FreeGC(dInfoPtr->scrollGC);
  301.     if (dInfoPtr->flags & REDRAW_PENDING) {
  302.         Tk_CancelIdleCall(DisplayText, (ClientData) textPtr);
  303.     }
  304.     ckfree((char *) dInfoPtr);
  305. }
  306.  
  307. /*
  308.  *----------------------------------------------------------------------
  309.  *
  310.  * GetStyle --
  311.  *
  312.  *    This procedure creates graphics contexts needed to display
  313.  *    text in a particular style, determined by "sValuePtr".  It
  314.  *    attempts to share style information as much as possible.
  315.  *
  316.  * Results:
  317.  *    The return value is a pointer to a Style structure that
  318.  *    corresponds to *sValuePtr.
  319.  *
  320.  * Side effects:
  321.  *    A new entry may be created in the style table for the widget.
  322.  *
  323.  *----------------------------------------------------------------------
  324.  */
  325.  
  326. static Style *
  327. GetStyle(textPtr, sValuePtr)
  328.     TkText *textPtr;        /* Overall information about text widget. */
  329.     StyleValues *sValuePtr;    /* Information about desired style. */
  330. {
  331.     Style *stylePtr;
  332.     Tcl_HashEntry *hPtr;
  333.     int new;
  334.     XGCValues gcValues;
  335.     unsigned long mask;
  336.  
  337.     /*
  338.      * Use an existing style if there's one around that matches.
  339.      */
  340.  
  341.     hPtr = Tcl_CreateHashEntry(&textPtr->dInfoPtr->styleTable,
  342.         (char *) sValuePtr, &new);
  343.     if (!new) {
  344.     stylePtr = (Style *) Tcl_GetHashValue(hPtr);
  345.     stylePtr->refCount++;
  346.     return stylePtr;
  347.     }
  348.  
  349.     /*
  350.      * No existing style matched.  Make a new one.
  351.      */
  352.  
  353.     stylePtr = (Style *) ckalloc(sizeof(Style));
  354.     stylePtr->refCount = 1;
  355.     if ((sValuePtr->border != NULL) && (sValuePtr->bgStipple != None)) {
  356.     gcValues.foreground = Tk_3DBorderColor(sValuePtr->border)->pixel;
  357.     gcValues.stipple = sValuePtr->bgStipple;
  358.     gcValues.fill_style = FillStippled;
  359.     stylePtr->bgGC = Tk_GetGC(textPtr->tkwin,
  360.         GCForeground|GCStipple|GCFillStyle, &gcValues);
  361.     } else {
  362.     stylePtr->bgGC = None;
  363.     }
  364.     mask = GCForeground|GCFont;
  365.     gcValues.foreground = sValuePtr->fgColor->pixel;
  366.     gcValues.font = sValuePtr->fontPtr->fid;
  367.     if (sValuePtr->fgStipple != None) {
  368.     gcValues.stipple = sValuePtr->fgStipple;
  369.     gcValues.fill_style = FillStippled;
  370.     mask |= GCStipple|GCFillStyle;
  371.     }
  372.     stylePtr->fgGC = Tk_GetGC(textPtr->tkwin, mask, &gcValues);
  373.     stylePtr->sValuePtr = (StyleValues *)
  374.         Tcl_GetHashKey(&textPtr->dInfoPtr->styleTable, hPtr);
  375.     stylePtr->hPtr = hPtr;
  376.     Tcl_SetHashValue(hPtr, stylePtr);
  377.     return stylePtr;
  378. }
  379.  
  380. /*
  381.  *----------------------------------------------------------------------
  382.  *
  383.  * FreeStyle --
  384.  *
  385.  *    This procedure is called when a Style structure is no longer
  386.  *    needed.  It decrements the reference count and frees up the
  387.  *    space for the style structure if the reference count is 0.
  388.  *
  389.  * Results:
  390.  *    None.
  391.  *
  392.  * Side effects:
  393.  *    The storage and other resources associated with the style
  394.  *    are freed up if no-one's still using it.
  395.  *
  396.  *----------------------------------------------------------------------
  397.  */
  398.  
  399. static void
  400. FreeStyle(stylePtr)
  401.     register Style *stylePtr;    /* Information about style to be freed. */
  402.  
  403. {
  404.     stylePtr->refCount--;
  405.     if (stylePtr->refCount == 0) {
  406.     if (stylePtr->bgGC != None) {
  407.         Tk_FreeGC(stylePtr->bgGC);
  408.     }
  409.     Tk_FreeGC(stylePtr->fgGC);
  410.     Tcl_DeleteHashEntry(stylePtr->hPtr);
  411.     ckfree((char *) stylePtr);
  412.     }
  413. }
  414.  
  415. /*
  416.  *----------------------------------------------------------------------
  417.  *
  418.  * ComputeStyleValues --
  419.  *
  420.  *    Given a list of tags that apply at a particular point, compute
  421.  *    the StyleValues that correspond to that set of tags.
  422.  *
  423.  * Results:
  424.  *    All of the fields of *sValuePtr get filled in to hold the
  425.  *    appropriate display information for the given set of tags
  426.  *    in the given widget.
  427.  *
  428.  * Side effects:
  429.  *    None.
  430.  *
  431.  *----------------------------------------------------------------------
  432.  */
  433.  
  434. static void
  435. ComputeStyleValues(textPtr, numTags, tagPtrPtr, sValuePtr)
  436.     TkText *textPtr;            /* Overall information for widget. */
  437.     int numTags;            /* Number of tags at *tagPtr. */
  438.     register TkTextTag **tagPtrPtr;    /* Pointer to array of tag pointers. */
  439.     register StyleValues *sValuePtr;    /* Pointer to structure to fill in. */
  440. {
  441.     register TkTextTag *tagPtr;
  442.  
  443.     /*
  444.      * The variables below keep track of the highest-priority specification
  445.      * that has occurred for each of the various fields of the StyleValues.
  446.      */
  447.  
  448.     int borderPrio, bgStipplePrio;
  449.     int fgPrio, fontPrio, fgStipplePrio;
  450.  
  451.     borderPrio = bgStipplePrio = -1;
  452.     fgPrio = fontPrio = fgStipplePrio = -1;
  453.     memset((VOID *) sValuePtr, 0, sizeof(StyleValues));
  454.     sValuePtr->fgColor = textPtr->fgColor;
  455.     sValuePtr->fontPtr = textPtr->fontPtr;
  456.  
  457.     /*
  458.      * Scan through all of the tags, updating the StyleValues to hold
  459.      * the highest-priority information.
  460.      */
  461.  
  462.     for ( ; numTags > 0; tagPtrPtr++, numTags--) {
  463.     tagPtr = *tagPtrPtr;
  464.     if ((tagPtr->border != NULL) && (tagPtr->priority > borderPrio)) {
  465.         sValuePtr->border = tagPtr->border;
  466.         sValuePtr->borderWidth = tagPtr->borderWidth;
  467.         sValuePtr->relief = tagPtr->relief;
  468.         borderPrio = tagPtr->priority;
  469.     }
  470.     if ((tagPtr->bgStipple != None)
  471.         && (tagPtr->priority > bgStipplePrio)) {
  472.         sValuePtr->bgStipple = tagPtr->bgStipple;
  473.         bgStipplePrio = tagPtr->priority;
  474.     }
  475.     if ((tagPtr->fgColor != None) && (tagPtr->priority > fgPrio)) {
  476.         sValuePtr->fgColor = tagPtr->fgColor;
  477.         fgPrio = tagPtr->priority;
  478.     }
  479.     if ((tagPtr->fontPtr != None) && (tagPtr->priority > fontPrio)) {
  480.         sValuePtr->fontPtr = tagPtr->fontPtr;
  481.         fontPrio = tagPtr->priority;
  482.     }
  483.     if ((tagPtr->fgStipple != None)
  484.         && (tagPtr->priority > fgStipplePrio)) {
  485.         sValuePtr->fgStipple = tagPtr->fgStipple;
  486.         fgStipplePrio = tagPtr->priority;
  487.     }
  488.     if (tagPtr->underline) {
  489.         sValuePtr->underline = 1;
  490.     }
  491.     }
  492. }
  493.  
  494. /*
  495.  *----------------------------------------------------------------------
  496.  *
  497.  * LayoutLine --
  498.  *
  499.  *    This procedure generates a linked list of one or more DLine
  500.  *    structures, which describe how to display everything in one
  501.  *    line of the text.
  502.  *
  503.  * Results:
  504.  *    The return value is a pointer to one or more DLine structures
  505.  *    linked into a linked list.  The structures are completely filled
  506.  *    in except for the y field, which the caller must supply.  Also,
  507.  *    the information at *tInfoPtr gets updated to refer to the state
  508.  *    just after the last character of the line.
  509.  *
  510.  * Side effects:
  511.  *    None.
  512.  *
  513.  *----------------------------------------------------------------------
  514.  */
  515.  
  516. static DLine *
  517. LayoutLine(textPtr, line, linePtr, tInfoPtr)
  518.     TkText *textPtr;        /* Overall information about text widget. */
  519.     int line;            /* Index of line to layout. */
  520.     TkTextLine *linePtr;    /* Line to layout (corresponds to line). */
  521.     TagInfo *tInfoPtr;        /* Information to help keep track of tags.
  522.                  * Caller must have initialized to correspond
  523.                  * to state just before start of line. */
  524. {
  525.     DLine *firstLinePtr;
  526.     DLine *lastLinePtr = NULL;    /* Initializations needed only to stop */
  527.     Chunk *lastChunkPtr = NULL;    /* compiler warnings. */
  528.     register DLine *dlPtr;
  529.     register Chunk *chunkPtr;
  530.     StyleValues styleValues;
  531.     int ch, charsThatFit, ascent, descent, x, maxX;
  532.  
  533.     firstLinePtr = NULL;
  534.  
  535.     /*
  536.      * Each iteration of the loop below creates one DLine structure.
  537.      */
  538.  
  539.     ch = 0;
  540.     while (1) {
  541.  
  542.     /*
  543.      * Create and initialize a new DLine structure.
  544.      */
  545.  
  546.     dlPtr = (DLine *) ckalloc(sizeof(DLine));
  547.     dlPtr->linePtr = linePtr;
  548.     dlPtr->y = 0;
  549.     dlPtr->oldY = -1;
  550.     dlPtr->chunkPtr = NULL;
  551.     dlPtr->nextPtr = NULL;
  552.     if (firstLinePtr == NULL) {
  553.         firstLinePtr = dlPtr;
  554.     } else {
  555.         lastLinePtr->nextPtr = dlPtr;
  556.     }
  557.     lastLinePtr = dlPtr;
  558.  
  559.     /*
  560.      * Each iteration of the loop below creates one Chunk for the
  561.      * new display line.
  562.      */
  563.  
  564.     x = textPtr->dInfoPtr->x;
  565.     maxX = textPtr->dInfoPtr->maxX;
  566.     ascent = descent = 0;
  567.     while (x < maxX) {
  568.         chunkPtr = (Chunk *) ckalloc(sizeof(Chunk));
  569.         chunkPtr->numChars = linePtr->numBytes - ch;
  570.         chunkPtr->text = linePtr->bytes + ch;
  571.         chunkPtr->x = x;
  572.         chunkPtr->nextPtr = NULL;
  573.         if (dlPtr->chunkPtr == NULL) {
  574.         dlPtr->chunkPtr = chunkPtr;
  575.         } else {
  576.         lastChunkPtr->nextPtr = chunkPtr;
  577.         }
  578.         lastChunkPtr = chunkPtr;
  579.  
  580.         /*
  581.          * Update the tag array to include any tag transitions up
  582.          * through the current position, then find the next position
  583.          * with a transition on a tag that impacts the way things are
  584.          * displayed.
  585.          */
  586.  
  587.         while (1) {
  588.         int affectsDisplay;
  589.         TkTextTag *tagPtr;
  590.  
  591.         if ((tInfoPtr->search.linePtr == NULL)
  592.             || (tInfoPtr->search.line1 > line)) {
  593.             break;
  594.         }
  595.         tagPtr = tInfoPtr->search.tagPtr;
  596.         affectsDisplay = TK_TAG_AFFECTS_DISPLAY(tagPtr);
  597.         if ((tInfoPtr->search.line1 < line)
  598.             || (tInfoPtr->search.ch1 <= ch)) {
  599.             if (affectsDisplay) {
  600.             ToggleTag(tInfoPtr, tagPtr);
  601.             }
  602.         } else {
  603.             if (affectsDisplay) {
  604.             chunkPtr->numChars = tInfoPtr->search.ch1 - ch;
  605.             break;
  606.             }
  607.         }
  608.         (void) TkBTreeNextTag(&tInfoPtr->search);
  609.         }
  610.  
  611.         /*
  612.          * Create style information for this chunk.
  613.          */
  614.  
  615.         ComputeStyleValues(textPtr, tInfoPtr->numTags, tInfoPtr->tagPtrs,
  616.             &styleValues);
  617.         chunkPtr->stylePtr = GetStyle(textPtr, &styleValues);
  618.  
  619.         /*
  620.          * See how many characters will fit on the line.  If they don't
  621.          * all fit, then a number of compensations may have to be made.
  622.          *
  623.          * 1. Make sure that at least one character is displayed on
  624.          *    each line.
  625.          * 2. In wrap mode "none", allow a partial character to be
  626.          *    displayed at the end of an incomplete line.
  627.          * 3. In wrap mode "word", search back to find the last space
  628.          *    character, and terminate the line just after that space
  629.          *    character.  This involves a couple of extra complexities:
  630.          *        - the last space may be several chunks back;  in this
  631.          *        case, delete all the chunks that are after the
  632.          *        space.
  633.          *          - if no words fit at all, then use character-wrap for
  634.          *        this DLine.
  635.          *        - have to reinitialize the tag search information, since
  636.          *        we may back up over tag toggles (they'll need to be
  637.          *        reconsidered on the next DLine).
  638.          */
  639.  
  640.         charsThatFit = TkMeasureChars(styleValues.fontPtr,
  641.             chunkPtr->text, chunkPtr->numChars, chunkPtr->x,
  642.             maxX, 0, &x);
  643.         if ((charsThatFit < chunkPtr->numChars) || (x >= maxX)) {
  644.         x = maxX;
  645.         chunkPtr->numChars = charsThatFit;
  646.         ch += charsThatFit;
  647.         if (ch < (linePtr->numBytes - 1)) {
  648.             if ((charsThatFit == 0) && (chunkPtr == dlPtr->chunkPtr)) {
  649.             chunkPtr->numChars = 1;
  650.             ch++;
  651.             } else if (textPtr->wrapMode == tkTextWordUid) {
  652.             if (isspace(chunkPtr->text[charsThatFit])) {
  653.                 ch += 1;    /* Include space on this line. */
  654.             } else {
  655.                 register Chunk *chunkPtr2;
  656.                 register char *p;
  657.                 Chunk *spaceChunkPtr;
  658.                 int count, space;
  659.  
  660.                 spaceChunkPtr = NULL;
  661.                 space = 0;
  662.                 for (chunkPtr2 = dlPtr->chunkPtr;
  663.                     chunkPtr2 != NULL;
  664.                     chunkPtr2 = chunkPtr2->nextPtr) {
  665.                 for (count = chunkPtr2->numChars - 1,
  666.                     p = chunkPtr2->text + count;
  667.                     count >= 0; count--, p--) {
  668.                     if (isspace(*p)) {
  669.                     spaceChunkPtr = chunkPtr2;
  670.                     space = count;
  671.                     break;
  672.                     }
  673.                 }
  674.                 }
  675.                 if (spaceChunkPtr != NULL) {
  676.                 spaceChunkPtr->numChars = space;
  677.                 ch = (spaceChunkPtr->text + space + 1)
  678.                     - linePtr->bytes;
  679.                 if (chunkPtr != spaceChunkPtr) {
  680.                     chunkPtr = spaceChunkPtr;
  681.                     if (tInfoPtr->tagPtrs != NULL) {
  682.                     ckfree((char *) tInfoPtr->tagPtrs);
  683.                     }
  684.                     tInfoPtr->tagPtrs = TkBTreeGetTags(
  685.                         textPtr->tree, dlPtr->linePtr, ch,
  686.                         &tInfoPtr->numTags);
  687.                     TkBTreeStartSearch(textPtr->tree, line,
  688.                         ch+1,
  689.                         TkBTreeNumLines(textPtr->tree), 0,
  690.                         (TkTextTag *) NULL,
  691.                         &tInfoPtr->search);
  692.                     (void) TkBTreeNextTag(&tInfoPtr->search);
  693.                     tInfoPtr->arraySize = tInfoPtr->numTags;
  694.                     while (chunkPtr->nextPtr != NULL) {
  695.                     chunkPtr2 = chunkPtr->nextPtr;
  696.                     chunkPtr->nextPtr = chunkPtr2->nextPtr;
  697.                     FreeStyle(chunkPtr2->stylePtr);
  698.                     ckfree((char *) chunkPtr2);
  699.                     }
  700.                 }
  701.                 }
  702.             }
  703.             } else if (textPtr->wrapMode == tkTextNoneUid) {
  704.             chunkPtr->numChars++;
  705.             ch++;
  706.             }
  707.         }
  708.         } else {
  709.         ch += chunkPtr->numChars;
  710.         }
  711.  
  712.         /*
  713.          * Update height information for use later in computing
  714.          * line's overall height and baseline.
  715.          */
  716.  
  717.         if (styleValues.fontPtr->ascent > ascent) {
  718.         ascent = styleValues.fontPtr->ascent;
  719.         }
  720.         if (styleValues.fontPtr->descent > descent) {
  721.         descent = styleValues.fontPtr->descent;
  722.         }
  723.     }
  724.  
  725.     dlPtr->height = ascent + descent;
  726.     dlPtr->baseline = ascent;
  727.  
  728.     /*
  729.      * Quit when every character but the last character (the newline)
  730.      * has been accounted for.  Also quit if the wrap mode is "none":
  731.      * this ignores all the characters that don't fit on the first
  732.      * line.
  733.      */
  734.  
  735.     if ((ch >= (linePtr->numBytes-1))
  736.         || (textPtr->wrapMode == tkTextNoneUid)) {
  737.         break;
  738.     }
  739.     }
  740.     return firstLinePtr;
  741. }
  742.  
  743. /*
  744.  *----------------------------------------------------------------------
  745.  *
  746.  * ToggleTag --
  747.  *
  748.  *    Update information about tags to reflect a transition on a
  749.  *    particular tag.
  750.  *
  751.  * Results:
  752.  *    The array at *tInfoPtr is modified to include tagPtr if it
  753.  *    didn't already or to exclude it if it used to include it.
  754.  *    The array will be reallocated to a larger size if needed.
  755.  *
  756.  * Side effects:
  757.  *    None.
  758.  *
  759.  *----------------------------------------------------------------------
  760.  */
  761.  
  762. static void
  763. ToggleTag(tInfoPtr, tagPtr)
  764.     register TagInfo *tInfoPtr;        /* Tag information to be updated. */
  765.     TkTextTag *tagPtr;            /* Tag to be toggled into or out of
  766.                      * *tInfoPtr. */
  767. {
  768.     register TkTextTag **tagPtrPtr;
  769.     int i;
  770.  
  771.     for (i = tInfoPtr->numTags, tagPtrPtr = tInfoPtr->tagPtrs;
  772.         i > 0; i--, tagPtrPtr++) {
  773.     if (*tagPtrPtr == tagPtr) {
  774.         tInfoPtr->numTags--;
  775.         *tagPtrPtr = tInfoPtr->tagPtrs[tInfoPtr->numTags];
  776.         return;
  777.     }
  778.     }
  779.  
  780.     /*
  781.      * Tag not currently in array.  Grow the array if necessary, then
  782.      * add the tag to it.
  783.      */
  784.  
  785.     if (tInfoPtr->numTags == tInfoPtr->arraySize) {
  786.     TkTextTag **newPtrs;
  787.  
  788.     newPtrs = (TkTextTag **) ckalloc((unsigned)
  789.         ((tInfoPtr->arraySize+10) * sizeof(TkTextTag *)));
  790.     if (tInfoPtr->tagPtrs != NULL) {
  791.         memcpy((VOID *) newPtrs, (VOID *) tInfoPtr->tagPtrs,
  792.             tInfoPtr->arraySize * sizeof(TkTextTag *));
  793.         ckfree((char *) tInfoPtr->tagPtrs);
  794.     }
  795.     tInfoPtr->tagPtrs = newPtrs;
  796.     tInfoPtr->arraySize += 10;
  797.     }
  798.     tInfoPtr->tagPtrs[tInfoPtr->numTags] = tagPtr;
  799.     tInfoPtr->numTags++;
  800. }
  801.  
  802. /*
  803.  *----------------------------------------------------------------------
  804.  *
  805.  * UpdateDisplayInfo --
  806.  *
  807.  *    This procedure is invoked to recompute some or all of the
  808.  *    DLine structures for a text widget.  At the time it is called
  809.  *    the DLine structures still left in the widget are guaranteed
  810.  *    to be correct (except for their y-coordinates), but there may
  811.  *    be missing structures (the DLine structures get removed as
  812.  *    soon as they are potentially out-of-date).
  813.  *
  814.  * Results:
  815.  *    None.
  816.  *
  817.  * Side effects:
  818.  *    Upon return, the DLine information for textPtr correctly reflects
  819.  *    the positions where characters will be displayed.  However, this
  820.  *    procedure doesn't actually bring the display up-to-date.
  821.  *
  822.  *----------------------------------------------------------------------
  823.  */
  824.  
  825. static void
  826. UpdateDisplayInfo(textPtr)
  827.     TkText *textPtr;        /* Text widget to update. */
  828. {
  829.     register DInfo *dInfoPtr = textPtr->dInfoPtr;
  830.     register DLine *dlPtr, *prevPtr, *dlPtr2;
  831.     TkTextLine *linePtr;
  832.     TagInfo tagInfo;
  833.     int line, y, maxY;
  834.  
  835.     if (!(dInfoPtr->flags & DINFO_OUT_OF_DATE)) {
  836.     return;
  837.     }
  838.     dInfoPtr->flags &= ~DINFO_OUT_OF_DATE;
  839.  
  840.     linePtr = textPtr->topLinePtr;
  841.     dlPtr = dInfoPtr->dLinePtr;
  842.     tagInfo.tagPtrs = TkBTreeGetTags(textPtr->tree, linePtr, 0,
  843.         &tagInfo.numTags);
  844.     tagInfo.arraySize = tagInfo.numTags;
  845.  
  846.     /*
  847.      * Tricky point:  initialize the tag search just *after* the first
  848.      * character in the line, since the tagInfo structure already has all
  849.      * the tags for the first character.
  850.      */
  851.  
  852.     line = TkBTreeLineIndex(linePtr);
  853.     TkBTreeStartSearch(textPtr->tree, line, 1, TkBTreeNumLines(textPtr->tree),
  854.         0, (TkTextTag *) NULL, &tagInfo.search);
  855.     TkBTreeNextTag(&tagInfo.search);
  856.     prevPtr = NULL;
  857.     y = dInfoPtr->y;
  858.     maxY = dInfoPtr->maxY;
  859.     while ((linePtr != NULL) && (y < maxY)) {
  860.     register DLine *newPtr;
  861.     /*
  862.      * See if the next DLine matches the next line we want to
  863.      * appear on the screen.  If so then we can just use its
  864.      * information.  If not then create new DLine structures
  865.      * for the desired line and insert them into the list.
  866.      */
  867.  
  868.     if ((dlPtr == NULL) || (dlPtr->linePtr != linePtr)) {
  869.         newPtr = LayoutLine(textPtr, line, linePtr, &tagInfo);
  870.         if (prevPtr == NULL) {
  871.         dInfoPtr->dLinePtr = newPtr;
  872.         } else {
  873.         prevPtr->nextPtr = newPtr;
  874.         }
  875.         for (dlPtr2 = newPtr; dlPtr2->nextPtr != NULL;
  876.             dlPtr2 = dlPtr2->nextPtr) {
  877.         /* Empty loop body. */
  878.         }
  879.         dlPtr2->nextPtr = dlPtr;
  880.         dlPtr = newPtr;
  881.     }
  882.  
  883.     /*
  884.      * Skip to the next line, and update the y-position while
  885.      * skipping.
  886.      */
  887.  
  888.     do {
  889.         dlPtr->y = y;
  890.         y += dlPtr->height;
  891.         prevPtr = dlPtr;
  892.         dlPtr = dlPtr->nextPtr;
  893.     } while ((dlPtr != NULL) && (dlPtr->linePtr == linePtr));
  894.     linePtr = TkBTreeNextLine(linePtr);
  895.     line++;
  896.     }
  897.  
  898.     /*
  899.      * Delete any DLine structures that don't fit on the screen and free
  900.      * up the tag array.
  901.      */
  902.  
  903.     FreeDLines(textPtr, dlPtr, (DLine *) NULL, 1);
  904.     if (tagInfo.tagPtrs != NULL) {
  905.     ckfree((char *) tagInfo.tagPtrs);
  906.     }
  907.  
  908.     /*
  909.      * Update the vertical scrollbar, if there is one.
  910.      */
  911.  
  912.     if (textPtr->yScrollCmd != NULL) {
  913.     int numLines, first, result, maxY, height;
  914.     char string[60];
  915.  
  916.     /*
  917.      * Count the number of text lines on the screen.
  918.      */
  919.  
  920.     maxY = 0;
  921.     for (numLines = 0, linePtr = NULL, dlPtr = dInfoPtr->dLinePtr;
  922.         dlPtr != NULL; dlPtr = dlPtr->nextPtr) {
  923.         if (dlPtr->linePtr != linePtr) {
  924.         numLines++;
  925.         linePtr = dlPtr->linePtr;
  926.         }
  927.         maxY = dlPtr->y + dlPtr->height;
  928.     }
  929.  
  930.     /*
  931.      * If the screen isn't completely full, then estimate the number of
  932.      * lines that would fit on it if it were full.
  933.      */
  934.  
  935.     height = dInfoPtr->maxY - dInfoPtr->y;
  936.     if (numLines == 0) {
  937.         numLines = height /
  938.             (textPtr->fontPtr->ascent + textPtr->fontPtr->descent);
  939.     } else if (maxY < height) {
  940.         numLines = (numLines * height)/maxY;
  941.     }
  942.     first = TkBTreeLineIndex(dInfoPtr->dLinePtr->linePtr);
  943.     sprintf(string, " %d %d %d %d", TkBTreeNumLines(textPtr->tree),
  944.         numLines, first, first+numLines-1);
  945.     result = Tcl_VarEval(textPtr->interp, textPtr->yScrollCmd, string,
  946.         (char *) NULL);
  947.     if (result != TCL_OK) {
  948.         TkBindError(textPtr->interp);
  949.     }
  950.     }
  951. }
  952.  
  953. /*
  954.  *----------------------------------------------------------------------
  955.  *
  956.  * FreeDLines --
  957.  *
  958.  *    This procedure is called to free up all of the resources
  959.  *    associated with one or more DLine structures.
  960.  *
  961.  * Results:
  962.  *    None.
  963.  *
  964.  * Side effects:
  965.  *    Memory gets freed and various other resources are released.
  966.  *
  967.  *----------------------------------------------------------------------
  968.  */
  969.  
  970. static void
  971. FreeDLines(textPtr, firstPtr, lastPtr, unlink)
  972.     TkText *textPtr;            /* Information about overall text
  973.                      * widget. */
  974.     register DLine *firstPtr;        /* Pointer to first DLine to free up. */
  975.     DLine *lastPtr;            /* Pointer to DLine just after last
  976.                      * one to free (NULL means everything
  977.                      * starting with firstPtr). */
  978.     int unlink;                /* 1 means DLines are currently linked
  979.                      * into the list rooted at
  980.                      * textPtr->dInfoPtr->dLinePtr and
  981.                      * they have to be unlinked.  0 means
  982.                      * just free without unlinking. */
  983. {
  984.     register Chunk *chunkPtr, *nextChunkPtr;
  985.     register DLine *nextDLinePtr;
  986.  
  987.     if (unlink) {
  988.     if (textPtr->dInfoPtr->dLinePtr == firstPtr) {
  989.         textPtr->dInfoPtr->dLinePtr = lastPtr;
  990.     } else {
  991.         register DLine *prevPtr;
  992.         for (prevPtr = textPtr->dInfoPtr->dLinePtr;
  993.             prevPtr->nextPtr != firstPtr; prevPtr = prevPtr->nextPtr) {
  994.         /* Empty loop body. */
  995.         }
  996.         prevPtr->nextPtr = lastPtr;
  997.     }
  998.     }
  999.     while (firstPtr != lastPtr) {
  1000.     nextDLinePtr = firstPtr->nextPtr;
  1001.     for (chunkPtr = firstPtr->chunkPtr; chunkPtr != NULL;
  1002.         chunkPtr = nextChunkPtr) {
  1003.         FreeStyle(chunkPtr->stylePtr);
  1004.         nextChunkPtr = chunkPtr->nextPtr;
  1005.         ckfree((char *) chunkPtr);
  1006.     }
  1007.     ckfree((char *) firstPtr);
  1008.     firstPtr = nextDLinePtr;
  1009.     }
  1010. }
  1011.  
  1012. /*
  1013.  *----------------------------------------------------------------------
  1014.  *
  1015.  * DisplayDLine --
  1016.  *
  1017.  *    This procedure is invoked to draw a single line on the
  1018.  *    screen.
  1019.  *
  1020.  * Results:
  1021.  *    None.
  1022.  *
  1023.  * Side effects:
  1024.  *    The line given by dlPtr is drawn at its correct position in
  1025.  *    textPtr's window.  Note that this is one *display* line, not
  1026.  *    one *text* line.
  1027.  *
  1028.  *----------------------------------------------------------------------
  1029.  */
  1030.  
  1031. static void
  1032. DisplayDLine(textPtr, dlPtr, pixmap)
  1033.     TkText *textPtr;        /* Text widget in which to draw line. */
  1034.     register DLine *dlPtr;    /* Information about line to draw. */
  1035.     Pixmap pixmap;        /* Pixmap to use for double-buffering.
  1036.                  * Caller must make sure it's large enough
  1037.                  * to hold line. */
  1038. {
  1039.     register Style *stylePtr;
  1040.     register StyleValues *sValuePtr;
  1041.     register Chunk *chunkPtr;
  1042.     DInfo *dInfoPtr = textPtr->dInfoPtr;
  1043.     Display *display;
  1044.     int width, height, count, x;
  1045.     XFontStruct *fontPtr;
  1046.  
  1047.     /*
  1048.      * First, clear the area of the line to the background color for the
  1049.      * text widget.
  1050.      */
  1051.  
  1052.     display = Tk_Display(textPtr->tkwin);
  1053.     Tk_Fill3DRectangle(display, pixmap, textPtr->border, 0, 0,
  1054.         Tk_Width(textPtr->tkwin), dlPtr->height, 0, TK_RELIEF_FLAT);
  1055.  
  1056.     /*
  1057.      * Next, cycle through all of the chunks in the line displaying
  1058.      * backgrounds.  We need to do two passes, one for the backgrounds
  1059.      * and one for the characters, because some characters (e.g. italics
  1060.      * with heavy slants) may cross background boundaries.  If some
  1061.      * backgrounds are drawn after some text, the later backgrounds may
  1062.      * obliterate parts of earlier characters.
  1063.      */
  1064.  
  1065.     for (chunkPtr = dlPtr->chunkPtr; chunkPtr != NULL;
  1066.         chunkPtr = chunkPtr->nextPtr) {
  1067.  
  1068.     /*
  1069.      * Draw a special background for this chunk if one is specified
  1070.      * in its style.  Two tricks here:
  1071.      * 1. if this is the last chunk in the line then extend the
  1072.      *    background across to the end of the line.
  1073.      * 2. if the background is stippled, then we have to draw the
  1074.      *    stippled part specially, since Tk_Fill3DRectangle doesn't
  1075.      *    do stipples.
  1076.      */
  1077.  
  1078.     stylePtr = chunkPtr->stylePtr;
  1079.     sValuePtr = stylePtr->sValuePtr;
  1080.     if (sValuePtr->border != NULL) {
  1081.         if (chunkPtr->nextPtr != NULL) {
  1082.         width = chunkPtr->nextPtr->x - chunkPtr->x;
  1083.         } else {
  1084.         width = Tk_Width(textPtr->tkwin) - chunkPtr->x;
  1085.         }
  1086.         if (stylePtr->bgGC != NULL) {
  1087.         XFillRectangle(display, pixmap, stylePtr->bgGC, chunkPtr->x,
  1088.             0, (unsigned int) width, (unsigned int) dlPtr->height);
  1089.         Tk_Draw3DRectangle(display, pixmap, sValuePtr->border,
  1090.             chunkPtr->x, 0, width, dlPtr->height,
  1091.             sValuePtr->borderWidth, sValuePtr->relief);
  1092.         } else {
  1093.         Tk_Fill3DRectangle(display, pixmap, sValuePtr->border,
  1094.             chunkPtr->x, 0, width, dlPtr->height,
  1095.             sValuePtr->borderWidth, sValuePtr->relief);
  1096.         }
  1097.     }
  1098.     }
  1099.  
  1100.     /*
  1101.      * If the insertion cursor is displayed on this line, then draw it
  1102.      * now, on top of the background but before the text.  As a special
  1103.      * hack to keep the cursor visible on mono displays, write the default
  1104.      * background in the cursor area (instead of nothing) when the cursor
  1105.      * isn't on.  Otherwise the selection would hide the cursor.
  1106.      */
  1107.  
  1108.     if ((textPtr->insertAnnotPtr->linePtr == dlPtr->linePtr)
  1109.         && (textPtr->state == tkTextNormalUid)
  1110.         && (textPtr->flags & GOT_FOCUS)) {
  1111.     for (chunkPtr = dlPtr->chunkPtr; chunkPtr != NULL;
  1112.         chunkPtr = chunkPtr->nextPtr) {
  1113.         count = textPtr->insertAnnotPtr->ch
  1114.             - (chunkPtr->text - dlPtr->linePtr->bytes);
  1115.         if (count < 0) {
  1116.         break;
  1117.         }
  1118.         if (count > chunkPtr->numChars) {
  1119.         continue;
  1120.         }
  1121.  
  1122.         /*
  1123.          * Deciding whether to display the cursor just after the last
  1124.          * character in a line is tricky because of various wrap
  1125.          * modes.  Do it unless we're in character wrap mode and
  1126.          * this line wraps, in which case it's better to display the
  1127.          * cursor on the next line.  For word wrap, there's an
  1128.          * undisplayed space character that the user must be able to
  1129.          * position the cursor in front of.  For no wrap, there's no
  1130.          * next line on which to display the cursor.
  1131.          */
  1132.         if ((count == chunkPtr->numChars)
  1133.             && (textPtr->wrapMode == tkTextCharUid)
  1134.             && (chunkPtr->text[count] != '\n')) {
  1135.         continue;
  1136.         }
  1137.         fontPtr = chunkPtr->stylePtr->sValuePtr->fontPtr;
  1138.         TkMeasureChars(fontPtr, chunkPtr->text, count, chunkPtr->x,
  1139.             (int) 1000000, 0, &x);
  1140.         if (textPtr->flags & INSERT_ON) {
  1141.         Tk_Fill3DRectangle(display, pixmap, textPtr->insertBorder,
  1142.             x - textPtr->insertWidth/2,
  1143.             dlPtr->baseline - fontPtr->ascent,
  1144.             textPtr->insertWidth,
  1145.             fontPtr->ascent + fontPtr->descent,
  1146.             textPtr->insertBorderWidth, TK_RELIEF_RAISED);
  1147.         } else if (DefaultDepthOfScreen(Tk_Screen(textPtr->tkwin)) == 1) {
  1148.         Tk_Fill3DRectangle(display, pixmap, textPtr->border,
  1149.             x - textPtr->insertWidth/2,
  1150.             dlPtr->baseline - fontPtr->ascent,
  1151.             textPtr->insertWidth,
  1152.             fontPtr->ascent + fontPtr->descent,
  1153.             0, TK_RELIEF_FLAT);
  1154.         }
  1155.  
  1156.     }
  1157.     }
  1158.  
  1159.     /*
  1160.      * Make another pass through all of the chunks to redraw all of
  1161.      * the text (and underlines, etc., if they're wanted).
  1162.      */
  1163.  
  1164.     for (chunkPtr = dlPtr->chunkPtr; chunkPtr != NULL;
  1165.         chunkPtr = chunkPtr->nextPtr) {
  1166.     stylePtr = chunkPtr->stylePtr;
  1167.     sValuePtr = stylePtr->sValuePtr;
  1168.     if (chunkPtr->numChars > 0) {
  1169.         TkDisplayChars(display, pixmap, stylePtr->fgGC, sValuePtr->fontPtr,
  1170.             chunkPtr->text, chunkPtr->numChars, chunkPtr->x,
  1171.             dlPtr->baseline, 0);
  1172.         if (sValuePtr->underline) {
  1173.         TkUnderlineChars(display, pixmap, stylePtr->fgGC,
  1174.             sValuePtr->fontPtr, chunkPtr->text, chunkPtr->x,
  1175.             dlPtr->baseline, 0, 0, chunkPtr->numChars-1);
  1176.         }
  1177.     }
  1178.     }
  1179.  
  1180.     /*
  1181.      * Copy the pixmap onto the screen.  If this is the last line on
  1182.      * the screen, only copy a piece of the line, so that it doesn't
  1183.      * overflow into the border area.  Another special trick:  copy the
  1184.      * padding area to the left of the line;  this is because the
  1185.      * insertion cursor sometimes overflows onto that area and we want
  1186.      * to get as much of the cursor as possible.
  1187.      */
  1188.  
  1189.     height = dlPtr->height;
  1190.     if ((height + dlPtr->y) > dInfoPtr->maxY) {
  1191.     height = dInfoPtr->maxY - dlPtr->y;
  1192.     }
  1193.     XCopyArea(display, pixmap, Tk_WindowId(textPtr->tkwin),
  1194.         dInfoPtr->copyGC, dInfoPtr->x - textPtr->padX, 0,
  1195.         dInfoPtr->maxX - (dInfoPtr->x - textPtr->padX),
  1196.         height, dInfoPtr->x - textPtr->padX, dlPtr->y);
  1197.     linesRedrawn++;
  1198. }
  1199.  
  1200. /*
  1201.  *----------------------------------------------------------------------
  1202.  *
  1203.  * DisplayText --
  1204.  *
  1205.  *    This procedure is invoked as a when-idle handler to update the
  1206.  *    display.  It only redisplays the parts of the text widget that
  1207.  *    are out of date.
  1208.  *
  1209.  * Results:
  1210.  *    None.
  1211.  *
  1212.  * Side effects:
  1213.  *    Information is redrawn on the screen.
  1214.  *
  1215.  *----------------------------------------------------------------------
  1216.  */
  1217.  
  1218. static void
  1219. DisplayText(clientData)
  1220.     ClientData clientData;    /* Information about widget. */
  1221. {
  1222.     register TkText *textPtr = (TkText *) clientData;
  1223.     DInfo *dInfoPtr = textPtr->dInfoPtr;
  1224.     Tk_Window tkwin;
  1225.     register DLine *dlPtr;
  1226.     Pixmap pixmap;
  1227.     int maxHeight;
  1228.     int bottomY = 0;        /* Initialization needed only to stop
  1229.                  * compiler warnings. */
  1230.  
  1231.     if ((textPtr->tkwin == NULL) || !Tk_IsMapped(textPtr->tkwin)
  1232.         || (dInfoPtr->maxX <= dInfoPtr->x)
  1233.         || (dInfoPtr->maxY <= dInfoPtr->y)) {
  1234.     goto done;
  1235.     }
  1236.     numRedisplays++;
  1237.  
  1238.     /*
  1239.      * Choose a new current item if that is needed (this could cause
  1240.      * event handlers to be invoked, hence the preserve/release calls
  1241.      * and the loop, since the handlers could conceivably necessitate
  1242.      * yet another current item calculation).  The tkwin check is because
  1243.      * the whole window could go away in the Tk_Release call.
  1244.      */
  1245.  
  1246.     while (dInfoPtr->flags & REPICK_NEEDED) {
  1247.     Tk_Preserve((ClientData) textPtr);
  1248.     dInfoPtr->flags &= ~REPICK_NEEDED;
  1249.     TkTextPickCurrent(textPtr, &textPtr->pickEvent);
  1250.     tkwin = textPtr->tkwin;
  1251.     Tk_Release((ClientData) textPtr);
  1252.     if (tkwin == NULL) {
  1253.         return;
  1254.     }
  1255.     }
  1256.  
  1257.     /*
  1258.      * First recompute what's supposed to be displayed.
  1259.      */
  1260.  
  1261.     UpdateDisplayInfo(textPtr);
  1262.  
  1263.     /*
  1264.      * Redraw the borders if that's needed.
  1265.      */
  1266.  
  1267.     if (dInfoPtr->flags & REDRAW_BORDERS) {
  1268.     Tk_Draw3DRectangle(Tk_Display(textPtr->tkwin),
  1269.         Tk_WindowId(textPtr->tkwin), textPtr->border,
  1270.         0, 0, Tk_Width(textPtr->tkwin), Tk_Height(textPtr->tkwin),
  1271.         textPtr->borderWidth, textPtr->relief);
  1272.     }
  1273.  
  1274.     /*
  1275.      * See if it's possible to bring some parts of the screen up-to-date
  1276.      * by scrolling (copying from other parts of the screen).
  1277.      */
  1278.  
  1279.     for (dlPtr = dInfoPtr->dLinePtr; dlPtr != NULL; dlPtr = dlPtr->nextPtr) {
  1280.     register DLine *dlPtr2;
  1281.     int offset, height;
  1282.  
  1283.     if ((dlPtr->oldY == -1) || (dlPtr->y == dlPtr->oldY)
  1284.         || ((dlPtr->oldY + dlPtr->height) > dInfoPtr->maxY)) {
  1285.         continue;
  1286.     }
  1287.  
  1288.     /*
  1289.      * This line is already drawn somewhere in the window so it only
  1290.      * needs to be copied to its new location.  See if there's a group
  1291.      * of lines that can all be copied together.
  1292.      */
  1293.  
  1294.     offset = dlPtr->y - dlPtr->oldY;
  1295.     height = dlPtr->height;
  1296.     for (dlPtr2 = dlPtr->nextPtr; dlPtr2 != NULL;
  1297.         dlPtr2 = dlPtr2->nextPtr) {
  1298.         if ((dlPtr2->oldY == -1)
  1299.             || ((dlPtr2->oldY + offset) != dlPtr2->y)
  1300.             || ((dlPtr2->oldY + dlPtr2->height) > dInfoPtr->maxY)) {
  1301.         break;
  1302.         }
  1303.         height += dlPtr2->height;
  1304.     }
  1305.  
  1306.     /*
  1307.      * Copy the information and update the lines to show that they've
  1308.      * been copied.  Reduce the height of the area being copied if
  1309.      * necessary to avoid overwriting the border area.
  1310.      */
  1311.  
  1312.     if ((dlPtr->y + height) > dInfoPtr->maxY) {
  1313.         height = dInfoPtr->maxY - dlPtr->y;
  1314.     }
  1315.     XCopyArea(Tk_Display(textPtr->tkwin), Tk_WindowId(textPtr->tkwin),
  1316.         Tk_WindowId(textPtr->tkwin), dInfoPtr->scrollGC,
  1317.         dInfoPtr->x - textPtr->padX, dlPtr->oldY,
  1318.         dInfoPtr->maxX - (dInfoPtr->x - textPtr->padX),
  1319.         height, dInfoPtr->x - textPtr->padX, dlPtr->y);
  1320.     numCopies++;
  1321.     while (1) {
  1322.         dlPtr->oldY = dlPtr->y;
  1323.         if (dlPtr->nextPtr == dlPtr2) {
  1324.         break;
  1325.         }
  1326.         dlPtr = dlPtr->nextPtr;
  1327.     }
  1328.  
  1329.     /*
  1330.      * It's possible that part of the area copied above was obscured.
  1331.      * To handle this situation, read expose-related events generated
  1332.      * during the XCopyArea operation.
  1333.      */
  1334.  
  1335.     while (1) {
  1336.         XEvent event;
  1337.  
  1338.         XWindowEvent(Tk_Display(textPtr->tkwin),
  1339.             Tk_WindowId(textPtr->tkwin), ExposureMask, &event);
  1340.         if (event.type == NoExpose) {
  1341.         break;
  1342.         } else if (event.type == GraphicsExpose) {
  1343.         TkTextRedrawRegion(textPtr, event.xgraphicsexpose.x,
  1344.             event.xgraphicsexpose.y, event.xgraphicsexpose.width,
  1345.             event.xgraphicsexpose.height);
  1346.         if (event.xgraphicsexpose.count == 0) {
  1347.             damagedCopies++;
  1348.             break;
  1349.         }
  1350.         } else if (event.type == Expose) {
  1351.         /*
  1352.          * A tricky situation.  This event must already have been
  1353.          * queued up before the XCopyArea was issued.  If the area
  1354.          * in this event overlaps the area copied, then some of the
  1355.          * bits that were copied were bogus.  The easiest way to
  1356.          * handle this is to issue two redisplays:  one for the
  1357.          * original area and one for the area shifted as if it was
  1358.          * in the copied area.
  1359.          */
  1360.  
  1361.         TkTextRedrawRegion(textPtr, event.xexpose.x,
  1362.             event.xexpose.y, event.xexpose.width,
  1363.             event.xexpose.height);
  1364.         TkTextRedrawRegion(textPtr, event.xexpose.x,
  1365.             event.xexpose.y + offset, event.xexpose.width,
  1366.             event.xexpose.height);
  1367.         } else {
  1368.         panic("DisplayText received unknown exposure event");
  1369.         }
  1370.     }
  1371.     }
  1372.  
  1373.     /*
  1374.      * Now we have to redraw the lines that couldn't be updated by
  1375.      * scrolling.  First, compute the height of the largest line and
  1376.      * allocate an off-screen pixmap to use for double-buffered
  1377.      * displays.
  1378.      */
  1379.  
  1380.     maxHeight = -1;
  1381.     for (dlPtr = textPtr->dInfoPtr->dLinePtr; dlPtr != NULL;
  1382.         dlPtr = dlPtr->nextPtr) {
  1383.     if ((dlPtr->height > maxHeight) && (dlPtr->oldY != dlPtr->y)) {
  1384.         maxHeight = dlPtr->height;
  1385.     }
  1386.     bottomY = dlPtr->y + dlPtr->height;
  1387.     }
  1388.     if (maxHeight >= 0) {
  1389.     pixmap = XCreatePixmap(Tk_Display(textPtr->tkwin),
  1390.         Tk_WindowId(textPtr->tkwin), Tk_Width(textPtr->tkwin),
  1391.         maxHeight, DefaultDepthOfScreen(Tk_Screen(textPtr->tkwin)));
  1392.     for (dlPtr = textPtr->dInfoPtr->dLinePtr; dlPtr != NULL;
  1393.         dlPtr = dlPtr->nextPtr) {
  1394.         if (dlPtr->oldY != dlPtr->y) {
  1395.         DisplayDLine(textPtr, dlPtr, pixmap);
  1396.         dlPtr->oldY = dlPtr->y;
  1397.         }
  1398.     }
  1399.     XFreePixmap(Tk_Display(textPtr->tkwin), pixmap);
  1400.     }
  1401.  
  1402.     /*
  1403.      * Lastly, see if we need to refresh the part of the window below
  1404.      * the last line of text (if there is any such area).
  1405.      */
  1406.  
  1407.     if (dInfoPtr->topOfEof > dInfoPtr->maxY) {
  1408.     dInfoPtr->topOfEof = dInfoPtr->maxY;
  1409.     }
  1410.     if (bottomY < dInfoPtr->topOfEof) {
  1411.     Tk_Fill3DRectangle(Tk_Display(textPtr->tkwin),
  1412.         Tk_WindowId(textPtr->tkwin), textPtr->border,
  1413.         dInfoPtr->x, bottomY, dInfoPtr->maxX - dInfoPtr->x,
  1414.         dInfoPtr->topOfEof-bottomY, 0, TK_RELIEF_FLAT);
  1415.     }
  1416.     dInfoPtr->topOfEof = bottomY;
  1417.     if (dInfoPtr->topOfEof > dInfoPtr->maxY) {
  1418.     dInfoPtr->topOfEof = dInfoPtr->maxY;
  1419.     }
  1420.  
  1421.     done:
  1422.     dInfoPtr->flags &= ~(REDRAW_PENDING|REDRAW_BORDERS);
  1423. }
  1424.  
  1425. /*
  1426.  *----------------------------------------------------------------------
  1427.  *
  1428.  * TkTextRedrawRegion --
  1429.  *
  1430.  *    This procedure is invoked to schedule a redisplay for a given
  1431.  *    region of a text widget.  The redisplay itself may not occur
  1432.  *    immediately:  it's scheduled as a when-idle handler.
  1433.  *
  1434.  * Results:
  1435.  *    None.
  1436.  *
  1437.  * Side effects:
  1438.  *    Information will eventually be redrawn on the screen.
  1439.  *
  1440.  *----------------------------------------------------------------------
  1441.  */
  1442.  
  1443.     /* ARGSUSED */
  1444. void
  1445. TkTextRedrawRegion(textPtr, x, y, width, height)
  1446.     TkText *textPtr;        /* Widget record for text widget. */
  1447.     int x, y;            /* Coordinates of upper-left corner of area
  1448.                  * to be redrawn, in pixels relative to
  1449.                  * textPtr's window. */
  1450.     int width, height;        /* Width and height of area to be redrawn. */
  1451. {
  1452.     register DLine *dlPtr;
  1453.     DInfo *dInfoPtr = textPtr->dInfoPtr;
  1454.     int maxY;
  1455.  
  1456.     /*
  1457.      * Find all lines that overlap the given region and mark them for
  1458.      * redisplay.
  1459.      */
  1460.  
  1461.     maxY = y + height;
  1462.     for (dlPtr = dInfoPtr->dLinePtr; dlPtr != NULL;
  1463.         dlPtr = dlPtr->nextPtr) {
  1464.     if (((dlPtr->y + dlPtr->height) > y) && (dlPtr->y < maxY)) {
  1465.         dlPtr->oldY = -1;
  1466.     }
  1467.     }
  1468.     if (dInfoPtr->topOfEof < maxY) {
  1469.     dInfoPtr->topOfEof = maxY;
  1470.     }
  1471.  
  1472.     /*
  1473.      * Schedule the redisplay operation if there isn't one already
  1474.      * scheduled.
  1475.      */
  1476.  
  1477.     if (!(dInfoPtr->flags & REDRAW_PENDING)) {
  1478.     dInfoPtr->flags |= REDRAW_PENDING;
  1479.     Tk_DoWhenIdle(DisplayText, (ClientData) textPtr);
  1480.     }
  1481.     if ((x < dInfoPtr->x) || (y < dInfoPtr->y)
  1482.         || ((x + width) > dInfoPtr->maxX) || (maxY > dInfoPtr->maxY)) {
  1483.     dInfoPtr->flags |= REDRAW_BORDERS;
  1484.     }
  1485. }
  1486.  
  1487. /*
  1488.  *----------------------------------------------------------------------
  1489.  *
  1490.  * TkTextLinesChanged --
  1491.  *
  1492.  *    This procedure is invoked when lines in a text widget are about
  1493.  *    to be modified in a way that changes how they are displayed (e.g.
  1494.  *    characters were inserted, the line was deleted, or tag information
  1495.  *    was changed).  This procedure must be called *before* a change is
  1496.  *    made, so that pointers to TkTextLines in the display information
  1497.  *    are still valid.
  1498.  *
  1499.  * Results:
  1500.  *    None.
  1501.  *
  1502.  * Side effects:
  1503.  *    The indicated lines will be redisplayed at some point in the
  1504.  *    future (the actual redisplay is scheduled as a when-idle handler).
  1505.  *
  1506.  *----------------------------------------------------------------------
  1507.  */
  1508.  
  1509. void
  1510. TkTextLinesChanged(textPtr, first, last)
  1511.     TkText *textPtr;        /* Widget record for text widget. */
  1512.     int first;            /* Index of first line that must be
  1513.                  * redisplayed. */
  1514.     int last;            /* Index of last line to redisplay. */
  1515. {
  1516.     DInfo *dInfoPtr = textPtr->dInfoPtr;
  1517.     DLine *firstPtr, *lastPtr;
  1518.  
  1519.     /*
  1520.      * Find the DLines corresponding to first and last+1.
  1521.      */
  1522.  
  1523.     firstPtr = FindDLine(dInfoPtr->dLinePtr, first);
  1524.     if (firstPtr == NULL) {
  1525.     return;
  1526.     }
  1527.     lastPtr = FindDLine(dInfoPtr->dLinePtr, last+1);
  1528.     if (firstPtr == lastPtr) {
  1529.     return;
  1530.     }
  1531.  
  1532.     /*
  1533.      * Delete all the DLines from first up through last (but not including
  1534.      * lastPtr, which points to the first line *outside* the range).
  1535.      */
  1536.  
  1537.     FreeDLines(textPtr, firstPtr, lastPtr, 1);
  1538.  
  1539.     /*
  1540.      * Schedule both a redisplay and a recomputation of display information.
  1541.      */
  1542.  
  1543.     if (!(dInfoPtr->flags & REDRAW_PENDING)) {
  1544.     Tk_DoWhenIdle(DisplayText, (ClientData) textPtr);
  1545.     }
  1546.     dInfoPtr->flags |= REDRAW_PENDING|DINFO_OUT_OF_DATE|REPICK_NEEDED;
  1547. }
  1548.  
  1549. /*
  1550.  *----------------------------------------------------------------------
  1551.  *
  1552.  * TkTextRedrawTag --
  1553.  *
  1554.  *    This procedure is invoked to request a redraw of all characters
  1555.  *    in a given range of characters that have a particular tag on or
  1556.  *    off.  It's called, for example, when characters are tagged or
  1557.  *    untagged, or when tag options change.
  1558.  *
  1559.  * Results:
  1560.  *    None.
  1561.  *
  1562.  * Side effects:
  1563.  *    Information on the screen may be redrawn, and the layout of
  1564.  *    the screen may change.
  1565.  *
  1566.  *----------------------------------------------------------------------
  1567.  */
  1568.  
  1569. void
  1570. TkTextRedrawTag(textPtr, line1, ch1, line2, ch2, tagPtr, withTag)
  1571.     TkText *textPtr;        /* Widget record for text widget. */
  1572.     int line1, ch1;        /* Index of first character in range of
  1573.                  * interest. */
  1574.     int line2, ch2;        /* Index of character just after last one
  1575.                  * in range of interest. */
  1576.     TkTextTag *tagPtr;        /* Information about tag. */
  1577.     int withTag;        /* 1 means redraw characters that have the
  1578.                  * tag, 0 means redraw those without. */
  1579. {
  1580.     register DLine *dlPtr;
  1581.     DLine *endPtr;
  1582.     int topLine, tagOn;
  1583.     TkTextSearch search;
  1584.     DInfo *dInfoPtr = textPtr->dInfoPtr;
  1585.  
  1586.     /*
  1587.      * Round up the starting position if it's before the first line
  1588.      * visible on the screen (we only care about what's on the screen).
  1589.      */
  1590.  
  1591.     dlPtr = dInfoPtr->dLinePtr;
  1592.     if (dlPtr == NULL) {
  1593.     return;
  1594.     }
  1595.     topLine = TkBTreeLineIndex(dlPtr->linePtr);
  1596.     if (topLine > line1) {
  1597.     line1 = topLine;
  1598.     ch1 = 0;
  1599.     }
  1600.  
  1601.     /* 
  1602.      * Initialize a search through all transitions on the tag, starting
  1603.      * with the first transition where the tag's current state is different
  1604.      * from what it will eventually be.
  1605.      */
  1606.  
  1607.     TkBTreeStartSearch(textPtr->tree, line1, ch1+1, line2, ch2,
  1608.         tagPtr, &search);
  1609.     tagOn = TkBTreeCharTagged(search.linePtr, ch1, tagPtr);
  1610.     if (tagOn != withTag) {
  1611.     if (!TkBTreeNextTag(&search)) {
  1612.         return;
  1613.     }
  1614.     }
  1615.  
  1616.     /*
  1617.      * Each loop through the loop below is for one range of characters
  1618.      * where the tag's current state is different than its eventual
  1619.      * state.  At the top of the loop, search contains information about
  1620.      * the first character in the range.
  1621.      */
  1622.  
  1623.     while (1) {
  1624.     /*
  1625.      * Find the first DLine structure in the range.
  1626.      */
  1627.  
  1628.     dlPtr = FindDLine(dlPtr, search.line1);
  1629.     if (dlPtr == NULL) {
  1630.         break;
  1631.     }
  1632.  
  1633.     /*
  1634.      * Find the first DLine structure that's past the end of the range.
  1635.      */
  1636.  
  1637.     if (TkBTreeNextTag(&search)) {
  1638.         endPtr = FindDLine(dlPtr,
  1639.             (search.ch1 > 0) ? (search.line1 + 1) : search.line1);
  1640.     } else {
  1641.         endPtr = FindDLine(dlPtr,
  1642.             (ch2 > 0) ? (search.line2 + 1) : search.line2);
  1643.     }
  1644.  
  1645.     /*
  1646.      * Delete all of the display lines in the range, so that they'll
  1647.      * be re-layed out and redrawn.
  1648.      */
  1649.  
  1650.     FreeDLines(textPtr, dlPtr, endPtr, 1);
  1651.     dlPtr = endPtr;
  1652.  
  1653.     /*
  1654.      * Find the first text line in the next range.
  1655.      */
  1656.  
  1657.     if (!TkBTreeNextTag(&search)) {
  1658.         break;
  1659.     }
  1660.     }
  1661.  
  1662.     /*
  1663.      * Lastly, schedule a redisplay and layout recalculation if they
  1664.      * aren't already pending.
  1665.      */
  1666.  
  1667.     if (!(dInfoPtr->flags & REDRAW_PENDING)) {
  1668.     Tk_DoWhenIdle(DisplayText, (ClientData) textPtr);
  1669.     }
  1670.     dInfoPtr->flags |= REDRAW_PENDING|DINFO_OUT_OF_DATE|REPICK_NEEDED;
  1671. }
  1672.  
  1673. /*
  1674.  *----------------------------------------------------------------------
  1675.  *
  1676.  * TkTextRelayoutWindow --
  1677.  *
  1678.  *    This procedure is called when something has happened that
  1679.  *    invalidates the whole layout of characters on the screen, such
  1680.  *    as a change in a configuration option for the overall text
  1681.  *    widget or a change in the window size.  It causes all display
  1682.  *    information to be recomputed and the window to be redrawn.
  1683.  *
  1684.  * Results:
  1685.  *    None.
  1686.  *
  1687.  * Side effects:
  1688.  *    All the display information will be recomputed for the window
  1689.  *    and the window will be redrawn.
  1690.  *
  1691.  *----------------------------------------------------------------------
  1692.  */
  1693.  
  1694. void
  1695. TkTextRelayoutWindow(textPtr)
  1696.     TkText *textPtr;        /* Widget record for text widget. */
  1697. {
  1698.     DInfo *dInfoPtr = textPtr->dInfoPtr;
  1699.  
  1700.     /*
  1701.      * Throw away all the current layout information.
  1702.      */
  1703.  
  1704.     FreeDLines(textPtr, dInfoPtr->dLinePtr, (DLine *) NULL, 1);
  1705.     dInfoPtr->dLinePtr = NULL;
  1706.  
  1707.     /*
  1708.      * Recompute some overall things for the layout.
  1709.      */
  1710.  
  1711.     dInfoPtr->x = textPtr->borderWidth + textPtr->padX;
  1712.     dInfoPtr->y = textPtr->borderWidth + textPtr->padY;
  1713.     dInfoPtr->maxX = Tk_Width(textPtr->tkwin) - dInfoPtr->x;
  1714.     dInfoPtr->maxY = Tk_Height(textPtr->tkwin) - dInfoPtr->y;
  1715.     dInfoPtr->topOfEof = dInfoPtr->maxY;
  1716.  
  1717.     if (!(dInfoPtr->flags & REDRAW_PENDING)) {
  1718.     Tk_DoWhenIdle(DisplayText, (ClientData) textPtr);
  1719.     }
  1720.     dInfoPtr->flags |= REDRAW_PENDING|REDRAW_BORDERS|DINFO_OUT_OF_DATE
  1721.         |REPICK_NEEDED;
  1722. }
  1723.  
  1724. /*
  1725.  *----------------------------------------------------------------------
  1726.  *
  1727.  * TkTextSetView --
  1728.  *
  1729.  *    This procedure is called to specify what lines are to be
  1730.  *    displayed in a text widget.
  1731.  *
  1732.  * Results:
  1733.  *    None.
  1734.  *
  1735.  * Side effects:
  1736.  *    The display will (eventually) be updated so that the line
  1737.  *    given by "line" is visible on the screen at the position
  1738.  *    determined by "pickPlace".
  1739.  *
  1740.  *----------------------------------------------------------------------
  1741.  */
  1742.  
  1743. void
  1744. TkTextSetView(textPtr, line, pickPlace)
  1745.     TkText *textPtr;        /* Widget record for text widget. */
  1746.     int line;            /* Number of line that is to appear somewhere
  1747.                  * in the window.  This line number must
  1748.                  * be a valid one in the file. */
  1749.     int pickPlace;        /* 0 means topLine must appear at top of
  1750.                  * screen.  1 means we get to pick where it
  1751.                  * appears:  minimize screen motion or else
  1752.                  * display line at center of screen. */
  1753. {
  1754.     DInfo *dInfoPtr = textPtr->dInfoPtr;
  1755.     register DLine *dlPtr, *dlPtr2;
  1756.     TkTextLine *linePtr;
  1757.     int curTopLine, curBotLine;
  1758.     int bottomY;
  1759.     TagInfo tagInfo;
  1760. #define CLOSE_LINES 5
  1761.  
  1762.     if (!pickPlace) {
  1763.     /*
  1764.      * The line must go at the top of the screen.  See if the new
  1765.      * topmost line is already somewhere on the screen.  If so then
  1766.      * delete all the DLine structures ahead of it.  Otherwise just
  1767.      * leave all the DLine's alone (if the new topmost line is above
  1768.      * the top of the current window, i.e. we're scrolling back towards
  1769.      * the beginning of the file we may be able to reuse some of the
  1770.      * information that's currently on the screen without redisplaying
  1771.      * it all.
  1772.      */
  1773.     
  1774.     dlPtr = FindDLine(dInfoPtr->dLinePtr, line);
  1775.     if ((dlPtr != NULL) && (dlPtr != dInfoPtr->dLinePtr)) {
  1776.         FreeDLines(textPtr, dInfoPtr->dLinePtr, dlPtr, 1);
  1777.     }
  1778.     
  1779.     textPtr->topLinePtr = TkBTreeFindLine(textPtr->tree, line);
  1780.     goto scheduleUpdate;
  1781.     }
  1782.  
  1783.     /*
  1784.      * We have to pick where to display the given line.  First, bring
  1785.      * the display information up to date and see if the line will be
  1786.      * completely visible in the current screen configuration.  If so
  1787.      * then there's nothing to do.
  1788.      */
  1789.  
  1790.     if (dInfoPtr->flags & DINFO_OUT_OF_DATE) {
  1791.     UpdateDisplayInfo(textPtr);
  1792.     }
  1793.     linePtr = TkBTreeFindLine(textPtr->tree, line);
  1794.     for (dlPtr = dInfoPtr->dLinePtr; ; dlPtr = dlPtr->nextPtr) {
  1795.     if (dlPtr->nextPtr == NULL) {
  1796.         break;
  1797.     }
  1798.     if ((dlPtr->linePtr == linePtr)
  1799.         && (dlPtr->nextPtr->linePtr != linePtr)) {
  1800.         break;
  1801.     }
  1802.     }
  1803.     if ((dlPtr->linePtr == linePtr)
  1804.         && ((dlPtr->y + dlPtr->height) <= dInfoPtr->maxY)) {
  1805.     return;
  1806.     }
  1807.  
  1808.     /*
  1809.      * The desired line isn't already on-screen.  See if it is within
  1810.      * a few lines of the top of the window.  If so then just make it
  1811.      * the top line on the screen.
  1812.      */
  1813.  
  1814.     bottomY = (dInfoPtr->y + dInfoPtr->maxY)/2;
  1815.     curTopLine = TkBTreeLineIndex(dInfoPtr->dLinePtr->linePtr);
  1816.     if (line < curTopLine) {
  1817.     if (line >= (curTopLine-CLOSE_LINES)) {
  1818.         textPtr->topLinePtr = TkBTreeFindLine(textPtr->tree, line);
  1819.         goto scheduleUpdate;
  1820.     }
  1821.     } else {
  1822.     /*
  1823.      * The desired line is below the bottom of the screen.  If it is
  1824.      * within a few lines of the bottom of the screen then position
  1825.      * it at the bottom of the screen. (At this point dlPtr points to
  1826.      * the last line on the screen)
  1827.      */
  1828.     
  1829.     curBotLine = TkBTreeLineIndex(dlPtr->linePtr);
  1830.     if (line <= (curBotLine+5)) {
  1831.         bottomY = dInfoPtr->maxY;
  1832.     }
  1833.     }
  1834.  
  1835.     /*
  1836.      * Our job now is arrange the display so that "line" appears as
  1837.      * low on the screen as possible but with its bottom no lower
  1838.      * than bottomY (bottomY is the bottom of the window if the
  1839.      * desired line is just below the current screen, otherwise it
  1840.      * is the center of the window.  Work upwards (through smaller
  1841.      * line numbers) computing how much space lines take, until we
  1842.      * fine the line that should be at the top of the screen.
  1843.      */
  1844.  
  1845.     for (textPtr->topLinePtr = linePtr = TkBTreeFindLine(textPtr->tree, line);
  1846.         ; line--, textPtr->topLinePtr = linePtr,
  1847.         linePtr = TkBTreeFindLine(textPtr->tree, line)) {
  1848.     tagInfo.tagPtrs = TkBTreeGetTags(textPtr->tree, linePtr, 0,
  1849.         &tagInfo.numTags);
  1850.     tagInfo.arraySize = tagInfo.numTags;
  1851.     TkBTreeStartSearch(textPtr->tree, line, 1, line+1, 0,
  1852.         (TkTextTag *) NULL, &tagInfo.search);
  1853.     TkBTreeNextTag(&tagInfo.search);
  1854.     dlPtr = LayoutLine(textPtr, line, linePtr, &tagInfo);
  1855.     for (dlPtr2 = dlPtr; dlPtr2 != NULL; dlPtr2 = dlPtr2->nextPtr) {
  1856.         bottomY -= dlPtr2->height;
  1857.     }
  1858.     FreeDLines(textPtr, dlPtr, (DLine *) NULL, 0);
  1859.     if (tagInfo.tagPtrs != NULL) {
  1860.         ckfree((char *) tagInfo.tagPtrs);
  1861.     }
  1862.     if ((bottomY <= 0) || (line <= 0)) {
  1863.         break;
  1864.     }
  1865.     }
  1866.  
  1867.     scheduleUpdate:
  1868.     if (!(dInfoPtr->flags & REDRAW_PENDING)) {
  1869.     Tk_DoWhenIdle(DisplayText, (ClientData) textPtr);
  1870.     }
  1871.     dInfoPtr->flags |= REDRAW_PENDING|DINFO_OUT_OF_DATE|REPICK_NEEDED;
  1872. }
  1873.  
  1874. /*
  1875.  *----------------------------------------------------------------------
  1876.  *
  1877.  * FindDLine --
  1878.  *
  1879.  *    This procedure is called to find the DLine corresponding to a
  1880.  *    given text line.
  1881.  *
  1882.  * Results:
  1883.  *    The return value is a pointer to the first DLine found in the
  1884.  *    list headed by dlPtr whose line number is greater or equal to
  1885.  *    line.  If there is no such line in the list then NULL is returned.
  1886.  *
  1887.  * Side effects:
  1888.  *    None.
  1889.  *
  1890.  *----------------------------------------------------------------------
  1891.  */
  1892.  
  1893. static DLine *
  1894. FindDLine(dlPtr, line)
  1895.     register DLine *dlPtr;    /* Pointer to first in list of DLines
  1896.                  * to search. */
  1897.     int line;            /* Line number in text that is desired. */
  1898. {
  1899.     TkTextLine *linePtr;
  1900.     int thisLine;
  1901.  
  1902.     if (dlPtr == NULL) {
  1903.     return NULL;
  1904.     }
  1905.     thisLine = TkBTreeLineIndex(dlPtr->linePtr);
  1906.     while (thisLine < line) {
  1907.     /*
  1908.      * This DLine isn't the right one.  Go on to the next DLine
  1909.      * (skipping multiple DLine's for the same text line).
  1910.      */
  1911.  
  1912.     linePtr = dlPtr->linePtr;
  1913.     do {
  1914.         dlPtr = dlPtr->nextPtr;
  1915.         if (dlPtr == NULL) {
  1916.         return NULL;
  1917.         }
  1918.     } while (dlPtr->linePtr == linePtr);
  1919.  
  1920.     /*
  1921.      * Step through text lines, keeping track of the line number
  1922.      * we're on, until we catch up to dlPtr (remember, there could
  1923.      * be gaps in the DLine list where DLine's have been deleted).
  1924.      */
  1925.  
  1926.     do {
  1927.         linePtr = TkBTreeNextLine(linePtr);
  1928.         thisLine++;
  1929.         if (linePtr == NULL) {
  1930.         panic("FindDLine reached end of text");
  1931.         }
  1932.     } while (linePtr != dlPtr->linePtr);
  1933.     }
  1934.     return dlPtr;
  1935. }
  1936.  
  1937. /*
  1938.  *----------------------------------------------------------------------
  1939.  *
  1940.  * TkTextCharAtLoc --
  1941.  *
  1942.  *    Given an (x,y) coordinate on the screen, find the location of
  1943.  *    the closest character to that location.
  1944.  *
  1945.  * Results:
  1946.  *    The return value is a pointer to the text line containing the
  1947.  *    character displayed closest to (x,y).  The value at *chPtr is
  1948.  *    overwritten with the index with that line of the closest
  1949.  *    character.
  1950.  *
  1951.  * Side effects:
  1952.  *    None.
  1953.  *
  1954.  *----------------------------------------------------------------------
  1955.  */
  1956.  
  1957. TkTextLine *
  1958. TkTextCharAtLoc(textPtr, x, y, chPtr)
  1959.     TkText *textPtr;        /* Widget record for text widget. */
  1960.     int x, y;            /* Pixel coordinates of point in widget's
  1961.                  * window. */
  1962.     int *chPtr;            /* Place to store index-within-line of
  1963.                  * closest character. */
  1964. {
  1965.     DInfo *dInfoPtr = textPtr->dInfoPtr;
  1966.     register DLine *dlPtr;
  1967.     register Chunk *chunkPtr;
  1968.     int count;
  1969.     int endX;
  1970.  
  1971.     /*
  1972.      * Make sure that all of the layout information about what's
  1973.      * displayed where on the screen is up-to-date.
  1974.      */
  1975.  
  1976.     if (dInfoPtr->flags & DINFO_OUT_OF_DATE) {
  1977.     UpdateDisplayInfo(textPtr);
  1978.     }
  1979.  
  1980.     /*
  1981.      * If the coordinates are above the top of the window, then adjust
  1982.      * them to refer to the upper-right corner of the window.
  1983.      */
  1984.  
  1985.     if (y < dInfoPtr->y) {
  1986.     y = dInfoPtr->y;
  1987.     x = dInfoPtr->x;
  1988.     } else if (y >= dInfoPtr->topOfEof) {
  1989.     y = dInfoPtr->topOfEof;
  1990.     x = dInfoPtr->maxX;
  1991.     }
  1992.     for (dlPtr = dInfoPtr->dLinePtr; dlPtr != NULL; dlPtr = dlPtr->nextPtr) {
  1993.     if (y > (dlPtr->y + dlPtr->height)) {
  1994.         if (dlPtr->nextPtr != NULL) {
  1995.         continue;
  1996.         }
  1997.  
  1998.         /*
  1999.          * The coordinates are off the bottom of the window.  Adjust
  2000.          * them to refer to the lower-right character on the window.
  2001.          */
  2002.  
  2003.         y = dlPtr->y;
  2004.         x = dInfoPtr->maxX;
  2005.     }
  2006.     for (chunkPtr = dlPtr->chunkPtr; ; chunkPtr = chunkPtr->nextPtr) {
  2007.         if ((chunkPtr->nextPtr == NULL) || (chunkPtr->nextPtr->x > x)) {
  2008.         break;
  2009.         }
  2010.     }
  2011.     count = TkMeasureChars(chunkPtr->stylePtr->sValuePtr->fontPtr,
  2012.         chunkPtr->text, chunkPtr->numChars, chunkPtr->x, x, 0, &endX);
  2013.     if (count >= chunkPtr->numChars) {
  2014.         /*
  2015.          * The point is off the end of the line.  Return the character
  2016.          * after the last one that fit, unless that character appears
  2017.          * as the first character on the next DLine or unless the last
  2018.          * one that fit extends beyond the edge of the window.
  2019.          */
  2020.  
  2021.         if ((dlPtr->nextPtr != NULL)
  2022.             && (dlPtr->nextPtr->chunkPtr->text
  2023.             == (chunkPtr->text + chunkPtr->numChars))) {
  2024.         count = chunkPtr->numChars-1;
  2025.         }
  2026.         if (endX >= dInfoPtr->maxX) {
  2027.         count = chunkPtr->numChars-1;
  2028.         }
  2029.     }
  2030.     *chPtr = count + (chunkPtr->text - dlPtr->linePtr->bytes);
  2031.     return dlPtr->linePtr;
  2032.     }
  2033.     panic("TkTextCharAtLoc ran out of lines");
  2034.     return (TkTextLine *) NULL;
  2035. }
  2036.